package com.common.database.mapper.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.zaxxer.hikari.HikariDataSource;

import lombok.extern.slf4j.Slf4j;
//import tk.mybatis.spring.mapper.MapperScannerConfigurer;

/**
 * @author liuchao
 * 多数据源自动装配
 */
@Service
@EnableConfigurationProperties(MultiDataSourceProperties.class)
@Slf4j
public class MultiDataSourceAutoConfiguration implements InitializingBean,DisposableBean, ImportBeanDefinitionRegistrar{

	private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
	
	@Autowired
	private DataSourceProperties dataSourceProperties;
	
	@Autowired
	private MultiDataSourceProperties multiDataSourceProperties;

	
	private Map<String,DataSource> dataSourceMap=new HashMap<String,DataSource>(5); 
	
	public MultiDataSourceAutoConfiguration() {
		super();
		log.info("初始化.........."+this.getClass().getSimpleName());
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		log.info("初始化多数据源配置");
		if(multiDataSourceProperties==null||multiDataSourceProperties.getMulti()==null) {
			log.info("多数据源配置为空");
			return ;
		}
		multiDataSourceProperties.getMulti().forEach((key,value)->{
			String name=value.getName();
			if(name==null) {
				value.setName(key);
			}
			//设置默认值
			setDefaultValue(value,dataSourceProperties);
			DataSource dataSource=buildDataSource(value);
			dataSourceMap.put(key, dataSource);
		});
	}
	
	private DataSource buildDataSource(DataSourceProperties properties) {
		HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
		if (StringUtils.hasText(properties.getName())) {
			dataSource.setPoolName(properties.getName());
		}
		return dataSource;
	}
	
	@SuppressWarnings("unchecked")
	private static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
		return (T) properties.initializeDataSourceBuilder().type(type).build();
	}
	
	private void setDefaultValue(DataSourceProperties source,DataSourceProperties destValue) {
		source.setBeanClassLoader(destValue.getClassLoader());
		try {
			source.afterPropertiesSet();
		} catch (Exception e) {
			log.warn("多数据源设置参数异常:"+e.toString());
			e.printStackTrace();
		}
	}

	@Override
	public void destroy() throws Exception {
		dataSourceMap.clear();
		dataSourceMap=null;
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		multiDataSourceProperties.getMulti().forEach((key,value)->{
			String name=value.getName();
			if(name==null) {
				value.setName(key);
			}
			//设置默认值
			setDefaultValue(value,dataSourceProperties);
			DataSource dataSource=buildDataSource(value);
			dataSourceMap.put(key, dataSource);
			
			String basePackage=value.getBasePackage();
			String locations=value.getMapperLocation();
			registMybatisSource(key,registry,locations,basePackage);
		});		
	}
	
    private void registMybatisSource(String dsName, BeanDefinitionRegistry registry, String locations, String basePackage) {
    	 String dsBeanName = dsName + "DataSource";
         String sqlSessionFactoryBeanName = dsName + "SqlSessionFactory";
         String sqlSessionTemplateBeanName = dsName + "SqlSessionTemplate";
         String mapperScannerBeanName = dsName + "MapperScanner";

         BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SqlSessionFactoryBean.class)
             .addPropertyReference("dataSource", dsBeanName)
             .addPropertyValue("mapperLocations", getResources(locations));

         registry.registerBeanDefinition(sqlSessionFactoryBeanName, builder.getBeanDefinition());


         builder = BeanDefinitionBuilder.rootBeanDefinition(SqlSessionTemplate.class)
             .addConstructorArgReference(sqlSessionFactoryBeanName);
         registry.registerBeanDefinition(sqlSessionTemplateBeanName, builder.getBeanDefinition());

         builder = BeanDefinitionBuilder.rootBeanDefinition(MapperScannerConfigurer.class)
             .addPropertyValue("sqlSessionTemplateBeanName", sqlSessionTemplateBeanName)
             .addPropertyValue("basePackage", basePackage);
         registry.registerBeanDefinition(mapperScannerBeanName, builder.getBeanDefinition());
    }	
	
    private Resource[] getResources(String location) {
        try {
          return resourceResolver.getResources(location);
        } catch (IOException e) {
        	log.warn("多数据源设置路径异常:"+e.toString());
        	e.printStackTrace();
        	return new Resource[0];
        }
     }
}
